/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/10/19
 *
 * History:
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>

#ifdef MELIS_OS
#include <finsh.h>
#endif

#include "osal_types.h"
#include "osal_mutex.h"
#include "osal_common.h"

#include "lindtunnel.h"
#include "ldt_error.h"
#include "sample_ldt_ioctrl_def.h"

#define LOG_TAG "sample_ldt_dev"
#include "osal_log.h"

#define LISENCE_KEY "TEST"

typedef struct _sample_ldt_dev_ctx_t
{
    int                     chn_running;
    pthread_t               chn_tid;
    lock_mutex              mutex;

    // for io test
    char                    str[32];
    int32_t                 ival;
    float                   fval;
} sample_ldt_dev_ctx_t;

typedef struct _sample_thread_param_t
{
    sample_ldt_dev_ctx_t *  p_ctx;
    int                     sid;
    int                     gid;
} sample_thread_param_t;

static int handler_on_add(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid, uint8_t * data)
{
    int ret = 0;
    sample_ldt_ioctrl_add_request_t * p_req = (sample_ldt_ioctrl_add_request_t *)data;
    logd("a: %d, b: %d", p_req->a, p_req->b);

    sample_ldt_ioctrl_add_response_t resp = {0};
    resp.result = p_req->a + p_req->b;
    ret = LDT_Dev_SendIOCtrl(sid, gid, SAMPLE_IOCTRL_ADD_RESP,
                            (int8_t *)&resp, sizeof(resp));
    if (ret < 0) {
        loge("LDT_IOCTRL_ADD_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static int handler_on_set_params(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid, uint8_t * data)
{
    int ret = 0;
    sample_ldt_ioctrl_set_params_request_t * p_req = (sample_ldt_ioctrl_set_params_request_t *)data;
    strcpy(p_ctx->str, p_req->str);
    p_ctx->ival = p_req->ival;
    p_ctx->fval = p_req->fval;
    logd("set params: str: %s, ival: %d, fval: %.1f", p_req->str, p_req->ival, p_req->fval);

    sample_ldt_ioctrl_set_params_response_t resp = {0};
    ret = LDT_Dev_SendIOCtrl(sid, gid, SAMPLE_IOCTRL_SET_PARAMS_RESP,
                            (int8_t *)&resp, sizeof(resp));
    if (ret < 0) {
        loge("LDT_IOCTRL_SET_PARAMS_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    return LDTER_OK;
}
static int handler_on_get_params(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid, uint8_t * data)
{
    int ret = 0;
    sample_ldt_ioctrl_get_params_request_t * p_req = (sample_ldt_ioctrl_get_params_request_t *)data;

    sample_ldt_ioctrl_get_params_response_t resp = {0};
    strcpy(resp.str, p_ctx->str);
    resp.ival = p_ctx->ival;
    resp.fval = p_ctx->fval;
    ret = LDT_Dev_SendIOCtrl(sid, gid, SAMPLE_IOCTRL_GET_PARAMS_RESP,
                            (int8_t *)&resp, sizeof(resp));
    if (ret < 0) {
        loge("LDT_IOCTRL_GET_PARAMS_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static int handler_channel_ioctrl(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid,
                                    int32_t cmd, uint8_t * data)
{
    int ret = 0;
    logd("Handle cmd: 0x%lx", cmd);
    switch(cmd)
    {
        case SAMPLE_IOCTRL_ADD_REQ:
        {
            return handler_on_add(p_ctx, sid, gid, data);
        }
        case SAMPLE_IOCTRL_SET_PARAMS_REQ:
        {
            return handler_on_set_params(p_ctx, sid, gid, data);
        }
        case SAMPLE_IOCTRL_GET_PARAMS_REQ:
        {
            return handler_on_get_params(p_ctx, sid, gid, data);
        }
        default:
            logw("unknown cmd: 0x%lx", cmd);
            break;
    }

    return 0;
}

static void * thread_channel_ioctrl(void *param)
{
    sample_thread_param_t * p_param = (sample_thread_param_t *)param;
    sample_ldt_dev_ctx_t * p_ctx = p_param->p_ctx;
    int sid = p_param->sid;
    int gid = p_param->gid;
    free(param);
    int ret = 0;
    int cmd = 0;
    uint8_t data[LDT_PKG_MAX_SIZE];
    int32_t size = 0;

    logd("thread_channel_ioctrl running ...");

    while(p_ctx->chn_running)
    {
        ret = LDT_Dev_RecvIOCtrl(sid, gid, &cmd, (uint8_t *)&data, &size, 1000);
        if (ret < 0)
        {
            continue;
        }

        handler_channel_ioctrl(p_ctx, sid, gid, cmd, (uint8_t *)&data);
    }

    logd("thread_channel_ioctrl exit");

    return 0;
}

static int32_t sample_ldt_dev_on_event(void * user, LDT_MSG_e msg, void * ext1, void * ext2)
{
    sample_ldt_dev_ctx_t * p_ctx = user;
    int ret = 0;

    switch (msg)
    {
        case LDT_MSG_LOGIN:
            logi("login success");
            break;
        case LDT_MSG_LOGOUT:
            logi("logout, something wrong ...");
            break;
        case LDT_MSG_SESSION_CONNECT:
            logi("client connect in, sid: %d", *(int*)ext1);
            break;
        case LDT_MSG_SESSION_DISCONN:
            logi("client disconnect, sid: %d", *(int*)ext1);
            break;
        case LDT_MSG_CHANNEL_GROUP_CREATED:
        {
            logi("new channel group sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            p_ctx->chn_running = 1;
            sample_thread_param_t * param = (sample_thread_param_t *)malloc(sizeof(sample_thread_param_t));
            param->p_ctx = p_ctx;
            param->sid = *(int*)ext1;
            param->gid = *(int*)ext2;
            ret = pthread_create(&p_ctx->chn_tid, NULL, thread_channel_ioctrl, (void *)param);
            if (ret) {
                loge("create thread_ldt_dev_login failed: %s", strerror(errno));
                return LDTER_FAILED;
            }
            break;
        }
        case LDT_MSG_CHANNEL_GROUP_DESTORY:
        {
            logi("channel group destory sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            p_ctx->chn_running = 0;
            if (p_ctx->chn_tid)
            {
                pthread_join(p_ctx->chn_tid, 0);
                p_ctx->chn_tid = 0;
            }
            break;
        }
        default:
            break;
    }

    return 0;
}

static int sample_ldt_dev_process(const char * uid, const char * password)
{
    sample_ldt_dev_ctx_t contex = {0};
    sample_ldt_dev_ctx_t * p_ctx = &contex;
    int ret = 0;

    init_lock(&p_ctx->mutex);

    LDT_Cfg_t config = {0};
    config.on_event = sample_ldt_dev_on_event;
    config.user = p_ctx;
    config.max_session = 4;
    config.timeout_ms = 5000;
    ret = LDT_Dev_Initialize(&config, LISENCE_KEY, uid, password);
    logi("LDT_Dev_Initialize ret: %d", ret);

    while(1)
    {
#ifdef MELIS_OS
        usleep(100000);
        continue;
#else
        int key_val = getchar();
        if (key_val <= 0) {
            continue;
        }

        if (key_val != '\n')
        {
            printf("********* key_val: %c ********** \n", key_val);
        }

        if (key_val == 'q' || key_val == 'Q')
        {
            break;
        }
#endif
    }

    destroy_lock(&p_ctx->mutex);
    LDT_Dev_Cleanup();

    return 0;
}

static void _sample_ldt_dev_usage(char * this)
{
    printf("\033[32m    Usage such as: \033[0m\n");
    printf("\033[32m        %s -u [uid] -p [password] \033[0m\n", this);
}

#ifdef MELIS_OS
static int main_melis(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
{
    int ret = 0;
    int i = 0;
    char * uid = "0001";
    char * password = "666666";

    if ((argc == 2)
        && (!strcmp(argv[1], "-h")))
    {
        _sample_ldt_dev_usage(argv[0]);
        return 0;
    }

    for (i = 1; i < argc; )
    {
        if (!strcmp(argv[i], "-u"))
        {
            uid = argv[i+1];
            i += 2;
        }
        else if (!strcmp(argv[i], "-p"))
        {
            password = argv[i+1];
            i += 2;
        }
        else
        {
            i++;
        }
    }

    sample_ldt_dev_process(uid, password);

    return 0;
}

#ifdef MELIS_OS
FINSH_FUNCTION_EXPORT_ALIAS(main_melis, __cmd_sample_ldt_dev, lindmpp sample);
#endif
